package common

import (
	"context"
	"fmt";"strings"
	//"reflect"
	"go.mongodb.org/mongo-driver/bson"
	//"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readconcern"
	"go.mongodb.org/mongo-driver/mongo/readpref"
	"go.mongodb.org/mongo-driver/mongo/writeconcern"
	//"ljquan.supor.com/packages/utils"
	"os"
	"time"
)


type MongoClient struct {
    C 	*mongo.Client
    T   context.Context
}

func CheckErr(err error) {
	if err != nil {
		if err == mongo.ErrNoDocuments {
			fmt.Println("Mongo/> Couldn't get data.")
			os.Exit(0)
		} else {
			fmt.Println(err)
			os.Exit(0)
		}
	}
}

/*
 连接MongoDB Server或者副本集
 */
func Mongo_ServerConn(url string, mode string) (*MongoClient, error) {
	
	var err error
	//var client *mongo.Client
	var want *readpref.ReadPref
	
	clnt := new(MongoClient)

	if mode == "secondary" {
		want, err = readpref.New(readpref.SecondaryMode) //表示只使用辅助节点
	}else if mode == "primary" {
		want, err = readpref.New(readpref.PrimaryMode) //表示只使用主节点
	}
	want = want
	if err != nil {
		fmt.Printf("probe1\n")
		CheckErr(err)
	}
	wc := writeconcern.New(writeconcern.WMajority())
	readconcern.Majority()
	//链接mongo服务
	opt := options.Client().ApplyURI(url)
	opt.SetLocalThreshold(3 * time.Second)     //只使用与mongo操作耗时小于3秒的
	opt.SetMaxConnIdleTime(5 * time.Second)    //指定连接可以保持空闲的最大毫秒数
	opt.SetMaxPoolSize(200)                    //使用最大的连接数
	opt.SetReadPreference(want)                //表示只使用辅助节点
	/* readconcern.Majority() 防脏读是Mongo 3.6以上才支持的Feature. */
	//opt.SetReadConcern(readconcern.Majority()) //指定查询应返回实例的最新数据确认为，已写入副本集中的大多数成员
	opt.SetWriteConcern(wc)                    //请求确认写操作传播到大多数mongod实例
	
	var cancel context.CancelFunc
	clnt.T, cancel = context.WithTimeout(context.Background(), 2*time.Second)
	cancel = cancel
    //defer cancel() // bug may happen

	if clnt.C, err = mongo.Connect(clnt.T, opt); err != nil {
		fmt.Printf("probe2\n")
		CheckErr(err)
	}

	//UseSession(client)
	//判断服务是否可用
	if mode == "secondary" {
		if err = clnt.C.Ping(clnt.T, readpref.Secondary()); err != nil {
			fmt.Printf("probe3\n")
			CheckErr(err)
		}
	}else if mode == "primary" {
		if err = clnt.C.Ping(clnt.T, readpref.Primary()); err != nil {
			fmt.Printf("probe4\n")
			CheckErr(err)
		}
	}

	return clnt,err
}

/*
 数据库对象
 */
func Mongo_ObjGetDatabase(clnt *MongoClient, db string ) *mongo.Database {
	return clnt.C.Database(db)
}

/*
 删除数据库
 */
func Mongo_DropDatabase(clnt *MongoClient, db string) {
	dbobj := Mongo_ObjGetDatabase(clnt, db)
	dbobj.Drop(clnt.T)
}

/*
 获取数据库中的集合
 */
func Mongo_ObjGetColl(clnt *MongoClient, db string, collname string) *mongo.Collection {
	return clnt.C.Database(db).Collection(collname)
}

/*
 删除集合
 */
func Mongo_ObjDelColl(clnt *MongoClient, coll *mongo.Collection) {
	coll.Drop(clnt.T)
}

func Mongo_DropColl(clnt *MongoClient, db string, collname string) {
	coll := Mongo_ObjGetColl(clnt, db, collname)
	Mongo_ObjDelColl(clnt, coll)
}

/*
 根据Document的一个键值对查询Document数据结构信息
 */
func Mongo_ObjInfoDoc(clnt *MongoClient, coll *mongo.Collection, k string, v string) error {
	var docinfo = make(map[string]interface{})
	err := coll.FindOne(clnt.T, bson.D{{k, v}}).Decode(&docinfo)
	fmt.Println(docinfo)
	fmt.Println("_id", docinfo["_id"])
	return err
}

func Mongo_InfoDoc(clnt *MongoClient, db string, collname string, 
			 k string, v string) error {
	var docinfo = make(map[string]interface{})
	coll := Mongo_ObjGetColl(clnt, db, collname)
	err := coll.FindOne(clnt.T, bson.D{{k, v}}).Decode(&docinfo)
	fmt.Println(docinfo)
	fmt.Println("_id", docinfo["_id"])
	return err
}

/*
 集合插入一条数据
 */
func Mongo_ObjCollAddDoc(clnt *MongoClient, coll *mongo.Collection, doc interface{}) error {
	var err error
	var insertOneRes    *mongo.InsertOneResult
	if insertOneRes, err = coll.InsertOne(clnt.T, doc); err != nil {
		CheckErr(err)
	}
	//fmt.Printf("Mongo/> Insert ID: %v\n", insertOneRes.InsertedID)
	insertOneRes.InsertedID = insertOneRes.InsertedID
	return err
}

func Mongo_CollAddDoc(clnt *MongoClient, db string, collname string, doc interface{}) error {
	var err error
	var insertOneRes    *mongo.InsertOneResult
	coll := Mongo_ObjGetColl(clnt, db, collname)
	if insertOneRes, err = coll.InsertOne(clnt.T, doc); err != nil {
		CheckErr(err)
	}
	//fmt.Printf("Mongo/> Insert ID: %v\n", insertOneRes.InsertedID)
	insertOneRes.InsertedID = insertOneRes.InsertedID
	return err
}

/*
 集合批量插入数据
 */
func Mongo_ObjCollAddDocs(clnt *MongoClient, coll *mongo.Collection, doc[] interface{} ) error {
	var err error
	var insertManyRes   *mongo.InsertManyResult
	if insertManyRes, err = coll.InsertMany(clnt.T, doc[0:]); err != nil {
		CheckErr(err)
		fmt.Printf("%v\n", err)
	}else{
		//fmt.Printf("Mongo/> Insert ID: %v\n", insertManyRes.InsertedIDs)
		insertManyRes.InsertedIDs = insertManyRes.InsertedIDs
	}
	
	return err	
}

func Mongo_CollAddDocs(clnt *MongoClient, db string, collname string, doc[] interface{}) error {
	var err error
	var insertManyRes   *mongo.InsertManyResult
	coll := Mongo_ObjGetColl(clnt, db, collname)
	if insertManyRes, err = coll.InsertMany(clnt.T, doc[0:]); err != nil {
		CheckErr(err)
		fmt.Printf("%v\n", err)
	}else{
		//fmt.Printf("Mongo/> Insert ID: %v\n", insertManyRes.InsertedIDs)
		insertManyRes.InsertedIDs = insertManyRes.InsertedIDs
	}
	
	return err	
}

/*
 根据Document的一个键值对查询Document数据
 */
func Mongo_ObjGetDoc(clnt *MongoClient, coll *mongo.Collection, k string, v string, doc interface {}) error {
	var err error
	if err = coll.FindOne(clnt.T, bson.D{{strings.ToLower(k), v}}).Decode(&doc); err != nil {
		CheckErr(err)
	}
	fmt.Printf("Mongo/> Get document data: %v\n", doc)
	return err
}

func Mongo_GetDoc(clnt *MongoClient, db string, collname string, 
			k1 string, v1 string, 
			k2 string, v2 string, 
			docStruct interface {}) (interface{}, error) {
	var err error
	coll := Mongo_ObjGetColl(clnt, db, collname)

	//t := reflect.TypeOf(docStruct);fmt.Printf("%v\n",t.Name())
	//getType := reflect.TypeOf(docStruct);fmt.Println(getType,getType.Name(),getType.Kind())
	//getValue := reflect.ValueOf(docStruct)
	//v := reflect.New(getType.Elem());fmt.Println(v)
	//fmt.Println("getValue",getValue)	

	if err = coll.FindOne(clnt.T, bson.D{{strings.ToLower(k1), v1},{strings.ToLower(k2),v2}}).Decode(&docStruct); err != nil {
		CheckErr(err)
	}
	
	//fmt.Printf("%v\n", docStruct)

	return docStruct,err
}


/* 
 超类：通过Golang interface实现
 */
type DocumentData interface {
	Doc() (interface{}, error)
	DocDel() (*mongo.DeleteResult, error)
	//GetDocObjId() (primitive.ObjectID, error)
}

/*
 实现子类
 */
type S1KV struct {
	Ks  string
	Vs  string
}

type S2KV struct {
	Ks1 string
	Vs1 string
	Ks2 string
	Vs2 string
}

type F1S1KV struct {
	Kf string
	Vf float32
	Ks string
	Vs string
}

type I1S1KV struct {
	Ks string
	Vs int
	Ki string
	Vi string
}

func (this *S1KV)Doc(clnt *MongoClient, db string, collname string,  
						docStruct interface {}) (interface{}, error) {
	var err error
	coll := Mongo_ObjGetColl(clnt, db, collname)
	if err = coll.FindOne(clnt.T, 
				bson.D{{strings.ToLower(this.Ks), this.Vs}}).Decode(&docStruct); err != nil {
		fmt.Printf("Mongo/> Document search failed, Operation type:[S1KV]\n")
		CheckErr(err)
	}
	return docStruct,err
}

func (this *F1S1KV)Doc(clnt *MongoClient, db string, collname string,  
						docStruct interface {}) (interface{}, error) {
	var err error
	coll := Mongo_ObjGetColl(clnt, db, collname)
	if err = coll.FindOne(clnt.T, 
				bson.D{{strings.ToLower(this.Kf), this.Vf},
					   {strings.ToLower(this.Ks), this.Vs}}).Decode(&docStruct); err != nil {
		fmt.Printf("Mongo/> Document search failed, Operation type:[F1S1KV]\n")
		CheckErr(err)
	}
	return docStruct,err
}

func (this *I1S1KV)Doc(clnt *MongoClient, db string, collname string,  
						docStruct interface {}) (interface{}, error) {
	var err error
	coll := Mongo_ObjGetColl(clnt, db, collname)
	if err = coll.FindOne(clnt.T, 
				bson.D{{strings.ToLower(this.Ki), this.Vi},
					   {strings.ToLower(this.Ks), this.Vs}}).Decode(&docStruct); err != nil {
       	fmt.Printf("Mongo/> Document search failed, Operation type:[I1S1KV]\n")
		CheckErr(err)
	}
	return docStruct,err
}

/*
  删除一条Document 数据
 */
func (this *S1KV)DocDel(clnt *MongoClient, db string, collname string,  
						docStruct interface {}) (*mongo.DeleteResult, error) {
	var err error
	coll := Mongo_ObjGetColl(clnt, db, collname)
	var delRes *mongo.DeleteResult
	if delRes, err = coll.DeleteOne(clnt.T, 
			 			bson.M{strings.ToLower(this.Ks): this.Vs}); err != nil {
		fmt.Printf("Mongo/> Document delete failed, Operation type:[S1KV]\n")
		CheckErr(err)
	}
	return delRes,err
}

func (this *F1S1KV)DocDel(clnt *MongoClient, db string, collname string,  
						docStruct interface {}) (*mongo.DeleteResult, error) {
	var err error
	coll := Mongo_ObjGetColl(clnt, db, collname)
	var delRes *mongo.DeleteResult
	if delRes, err = coll.DeleteOne(clnt.T, 
			 	bson.M{strings.ToLower(this.Kf): this.Vf, 
			 		   strings.ToLower(this.Ks): this.Vs} ); err != nil {
		fmt.Printf("Mongo/> Document delete failed, Operation type:[F1S1KV]\n")
		CheckErr(err)
	}
	return delRes,err
}

func (this *I1S1KV)DocDel(clnt *MongoClient, db string, collname string,  
						docStruct interface {}) (*mongo.DeleteResult, error) {
	var err error
	coll := Mongo_ObjGetColl(clnt, db, collname)
	var delRes *mongo.DeleteResult
	if delRes, err = coll.DeleteOne(clnt.T, 
			 	bson.M{strings.ToLower(this.Ki): this.Vi, 
			 		   strings.ToLower(this.Ks): this.Vs} ); err != nil {
		fmt.Printf("Mongo/> Document delete failed, Operation type:[I1S1KV]\n")
		CheckErr(err)
	}
	return delRes,err
}

type UpdateDocumentData interface {
	DocUpdate()
}

type S1NewVs struct{
	Ks string
	Vs string
	NVs string
}

type F1NewVf struct{
	Kf string
	Vf float32
	NVf float32
}

type F1S1NewVf struct{
	Kf string
	Vf float32
	Ks string
	Vs string
	NVf float32
}

/*
 更新Document数据
 */
func (this *S1NewVs)DocUpdate(clnt *MongoClient, db string, collname string,
				docStruct interface {}) (error) {
	var err error
	coll := Mongo_ObjGetColl(clnt, db, collname)
    if err = coll.FindOneAndUpdate(clnt.T, bson.D{{strings.ToLower(this.Ks), this.Vs}}, 
    							bson.M{"$set": bson.M{strings.ToLower(this.Ks): this.NVs}}).Decode(&docStruct); 
    							err != nil {
        CheckErr(err)
    }
	return err					
}

func (this *F1NewVf)DocUpdate(clnt *MongoClient, db string, collname string,
				docStruct interface {}) (error) {
	var err error
	coll := Mongo_ObjGetColl(clnt, db, collname)
    if err = coll.FindOneAndUpdate(clnt.T, bson.D{{strings.ToLower(this.Kf), this.Vf}}, 
    							bson.M{"$set": bson.M{strings.ToLower(this.Kf): this.NVf}}).Decode(&docStruct); 
    							err != nil {
        CheckErr(err)
    }
	return err					
}

func (this *F1S1NewVf)DocUpdate(clnt *MongoClient, db string, collname string,
				docStruct interface {}) (error) {
	var err error
	coll := Mongo_ObjGetColl(clnt, db, collname)
    if err = coll.FindOneAndUpdate(clnt.T, bson.D{{strings.ToLower(this.Kf), this.Vf}, {strings.ToLower(this.Ks), this.Vs}}, 
    							bson.M{"$set": bson.M{strings.ToLower(this.Kf): this.NVf}}).Decode(&docStruct); 
    							err != nil {
        fmt.Printf("Mongo/> Document update failed, Operation type:[F1S1NewVf]\n")
        CheckErr(err)

    }
	return err					
}